package com.example.scribe.backend.handlers;

import com.example.scribe.SyntaxNode;
import com.example.scribe.backend.ASTUtils;
import com.example.scribe.backend.BytecodeOps;
import com.example.scribe.backend.ExpressionEmitter;
import com.example.scribe.backend.LocalVariableAllocator;
import com.example.scribe.backend.StatementEmitter;
import com.example.scribe.backend.StatementNodeHandler;
import net.bytebuddy.implementation.bytecode.StackManipulation;
import net.bytebuddy.implementation.bytecode.StackManipulation.Compound;

/**
 * Handler for variable declaration statements.
 * Processes VariableDecl nodes and returns StackManipulation for initialization and storage.
 */
public final class VariableDeclStatementHandler implements StatementNodeHandler {
    public VariableDeclStatementHandler() {
    }

    @Override
    public boolean canHandle(String nodeType) {
        return "VariableDecl".equals(nodeType);
    }

    @Override
    public StackManipulation handle(SyntaxNode node, String generatedClassName, LocalVariableAllocator locals, ExpressionEmitter exprEmitter, StatementEmitter statementEmitter) {
        if (node == null) {
            return StackManipulation.Trivial.INSTANCE;
        }

        String name = null;
        SyntaxNode init = null;

        // Try both "Identifier" and "identifier" (case variations)
        for (SyntaxNode c : node.children()) {
            if (ASTUtils.isIdentifier(c)) {
                name = c.text();
            }
            if ("expression".equals(c.type())) {
                init = c;
            }
        }

        if (name != null && init != null) {
            int localIndex = locals.allocateLocal(name);
            StackManipulation initEmit = exprEmitter.emit(init, generatedClassName, locals);
            
            if (initEmit == StackManipulation.Trivial.INSTANCE) {
                // Try fallback for init expression
                StackManipulation fallback = findIdentifierOrNumber(init, generatedClassName, locals);
                if (fallback != StackManipulation.Trivial.INSTANCE) {
                    initEmit = fallback;
                } else {
                    initEmit = BytecodeOps.pushInt(0);
                }
            }

            // Return: init expression, then store to local
            return new Compound(initEmit, BytecodeOps.storeInt(localIndex));
        }

        return StackManipulation.Trivial.INSTANCE;
    }

    /**
     * Helper method to find identifier or number in expression subtree.
     */
    private StackManipulation findIdentifierOrNumber(SyntaxNode node, String generatedClassName, LocalVariableAllocator locals) {
        if (node == null) {
            return StackManipulation.Trivial.INSTANCE;
        }

        // Check for identifier as local variable
        if (ASTUtils.isIdentifier(node)) {
            String name = node.text();
            if (name != null && locals.hasName(name)) {
                return BytecodeOps.loadInt(locals.getIndexByName(name));
            }
        }

        // Check for number
        if (ASTUtils.isNumber(node)) {
            try {
                int v = Integer.parseInt(node.text());
                return BytecodeOps.pushInt(v);
            } catch (NumberFormatException e) {
                // continue
            }
        }

        // Also check for identifier in primary nodes
        if ("primary".equals(node.type())) {
            for (SyntaxNode child : node.children()) {
                if (ASTUtils.isIdentifier(child)) {
                    String name = child.text();
                    if (name != null && locals.hasName(name)) {
                        return BytecodeOps.loadInt(locals.getIndexByName(name));
                    }
                }
            }
        }

        // Recursively search children
        for (SyntaxNode child : node.children()) {
            StackManipulation found = findIdentifierOrNumber(child, generatedClassName, locals);
            if (found != StackManipulation.Trivial.INSTANCE) {
                return found;
            }
        }

        return StackManipulation.Trivial.INSTANCE;
    }
}

